
let fs = require("fs")
let path = require('path');
let  Module  = require("./module")
let MagicString = require('magic-string');
const { keys, hasOwnProperty } = require('./utils')
const walk = require('./ast/walk');

class Bundle{
    constructor(options){
        // 兼容没有添加js后缀，或者传递路径是相对路径的情况
        this.entryPath = path.resolve(options.entry.replace(/\.js$/, '') + '.js');
    }
    /**
     * 构建
     * @param {*} filename 输出路径
     */
    build(filename){
        let entryModule = this.fetchModule(this.entryPath)
        //获取入口模块所有的语句节点，展开所有语句。处理export
        this.statements = entryModule.expandAllStatements(true); 
        // 解决命名冲突
        this.deConflict();
        // 根据这些语句节点生产新的源代码
        const { code } = this.generate({});
        // 写入目标文件
        fs.writeFileSync(filename, code); 
    }
    /**
     * 解决命名冲突
     */
    deConflict() {
        const defines = {};//定义的变量
        const conflict = {};//变量我冲突的变量
        this.statements.forEach(statement => {
            keys(statement._defines).forEach(name => {
                if ((hasOwnProperty(defines, name))) { //如果已存在
                    conflict[name] = true;
                } else {
                    defines[name] = [];
                }
                defines[name].push(statement._module);
            });
        });
        keys(conflict).forEach(name => {
            const modules = defines[name];
            modules.pop();//弹出最后一个，最后一个模块不需要重命名
            //modules.shift();//弹出第一个，第一个模块不需要重命名
            modules.forEach((module, index) => {
                module.rename(name, `${name}$${modules.length - index}`);
            });
        });
    }
        
    /**
     * 获取模块
     * @param {*} importee  模块的路径，可能是绝对路径，也可能是相对路径  msg.js
     * @param {*} importer  从哪个模块导入 main.js
     * 
     */
    fetchModule(importee,importer){
        let route;
        //如果importer为空说明这是一个入口模块，没有引入它的父模块
        if(!importer){
            route = importee;
        }else{
            if (path.isAbsolute(importee)) {  //如果是绝对路径
                route = importee;
            } else if (importee[0] === '.') { //如果是相对路径
                route = path.resolve(path.dirname(importer), importee.replace(/\.js$/, '') + '.js');
            }
        }
        if(route){
            let code = fs.readFileSync(route,'utf8')
            const module = new Module({
                code, //模块源代码
                path:route, //模块绝对路径
                bundle:this //bundle全局只有一个，类似webpack的compiler
            })
            return module
        }
    }
    generate(options){
        let magicString = new MagicString.Bundle();
        this.statements.forEach(statement => {
            let replacements = {};
            keys(statement._dependsOn)//用到的变量
                .concat(keys(statement._defines))//定义的变量
                .forEach(name => {
                    const canonicalName = statement._module.getCanonicalName(name); //返回新的名字
                    if (name !== canonicalName) { //如果新名字和老名字不一样
                        replacements[name] = canonicalName;
                    }
                });
            const source = statement._source.clone(); //克隆
            // 删除export
            if (/^Export/.test(statement.type)) {
                source.remove(statement.start, statement.declaration.start);
            }
            replaceIdentifiers(statement, source, replacements);

            // 把每个语法树节点代码添加到magicString中
            magicString.addSource({
                content: source,
                separator: '\n'
            })
        })
        return { code: magicString.toString() }
    }
}
function replaceIdentifiers(statement, source, replacements) {
    walk(statement, {
        enter(node) {
            if (node.type === 'Identifier') {
                if (node.name && hasOwnProperty(replacements, node.name)) {
                    //source = var age1 = age + '1';
                    //source = var age1 = age$1 + '1';
                    source.overwrite(node.start, node.end, replacements[node.name]);
                }
            }
        }
    });
}
module.exports = Bundle